#include "Board.h"
#include <QPainter>
#include<QMouseEvent>
#include "ui_Board.h"

#define GetRowCol(__row, __col, __id) \
    int __row = _s[__id]._row;\
    int __col = _s[__id]._col
// 在c++中宏定义变成多行时需要加上"\"作为换行

Board::Board(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Board){
    init(true);
   _timer = new QTimer;  //初始化定时器
   _timeRecord  = new QTime(0, 0, 0); //初始化时间
   _isStart = false;  //初始为还未计时

   connect(_timer,SIGNAL(timeout()),this,SLOT(updateTime()));
   ui->setupUi(this);
}

Board::~Board(){delete ui;}

void Board::init(bool bRedSide){
    for(int i=0; i<32; ++i){
           _s[i].init(i);
       }
       if(bRedSide){
           for(int i=0; i<32; ++i){
               _s[i].rotate();
           }
       }
       _selectid = -1;
       _clickid = -1;
       _bRedTurn = true;
       _isOverGame = false;
       _bSide = bRedSide;
       update();
}
void Board :: paintEvent(QPaintEvent *){
    QPainter painter(this);
    double d = 40;
    _r = d/2;
    //画10条横线
    for (int i = 1; i <= 10; i++) {
     painter.drawLine(QPoint(d, i*d),QPoint(9*d, i*d));
    }
    //画9条竖线
    for (int i = 1; i <= 9; i++) {
        if(i == 1 || i == 9)
            painter.drawLine(QPoint(i*d, d),QPoint(i*d, 10*d));
        else{
            painter.drawLine(QPoint(i*d, d),QPoint(i*d, 5*d));
            painter.drawLine(QPoint(i*d, 6*d),QPoint(i*d, 10*d));
        }
    }
    //画将帅所在位置的斜线
    painter.drawLine(QPoint(4*d,1*d),QPoint(6*d,3*d));
    painter.drawLine(QPoint(6*d,1*d),QPoint(4*d,3*d));

    painter.drawLine(QPoint(4*d,8*d),QPoint(6*d,10*d));
    painter.drawLine(QPoint(6*d,8*d),QPoint(4*d,10*d));


    QRect rect1(1.5*d, 5*d, d, d);
    QRect rect2(2.5*d, 5*d, d, d);
    QRect rect3(6.5*d, 5*d, d, d);
    QRect rect4(7.5*d, 5*d, d, d);
    painter.setFont(QFont("隶书", _r, 800));
    painter.drawText(rect1, "楚", QTextOption(Qt::AlignCenter));
    painter.drawText(rect2, "河", QTextOption(Qt::AlignCenter));
    painter.drawText(rect3, "汉", QTextOption(Qt::AlignCenter));
    painter.drawText(rect4, "界", QTextOption(Qt::AlignCenter));

    //画兵、炮位置标记
    drawInitPosition(painter);

    //画棋子
    for (int i = 0; i < 32; i++) {
        drawStone(painter, i);
    }
}
void Board::drawInitPosition(QPainter &p, int row, int col){
    QPoint pt = center(row, col);
    QPoint off = QPoint(_r/6, _r/6);
    int len = _r/3; //线的长度

    QPoint ptStart;//折线交点
    QPoint ptEnd;//画线结束点

    if(col != 0){
        /* 左上角 */
        ptStart = QPoint(pt.x() - off.x(), pt.y() - off.y());
        ptEnd = ptStart + QPoint(-len, 0);
        p.drawLine(ptStart, ptEnd);
        ptEnd = ptStart + QPoint(0, -len);
        p.drawLine(ptStart, ptEnd);

        /* 左下角 */
        ptStart = QPoint(pt.x() - off.x(), pt.y() + off.y());
        ptEnd = ptStart + QPoint(-len, 0);
        p.drawLine(ptStart, ptEnd);
        ptEnd = ptStart + QPoint(0, +len);
        p.drawLine(ptStart, ptEnd);
    }
    if(col != 8){
        /* 右上角 */
        ptStart = QPoint(pt.x() + off.x(), pt.y() - off.y());
        ptEnd = ptStart + QPoint(+len, 0);
        p.drawLine(ptStart, ptEnd);
        ptEnd = ptStart + QPoint(0, -len);
        p.drawLine(ptStart, ptEnd);

        /* 右下角 */
        ptStart = QPoint(pt.x() + off.x(), pt.y() + off.y());
        ptEnd = ptStart + QPoint(+len, 0);
        p.drawLine(ptStart, ptEnd);
        ptEnd = ptStart + QPoint(0, +len);
        p.drawLine(ptStart, ptEnd);
    }
}

void Board::drawInitPosition(QPainter &p){
    //上半部分兵的位置
    drawInitPosition(p, 3, 0);
    drawInitPosition(p, 3, 2);
    drawInitPosition(p, 3, 4);
    drawInitPosition(p, 3, 6);
    drawInitPosition(p, 3, 8);

    //下半部分兵的位置
    drawInitPosition(p, 6, 0);
    drawInitPosition(p, 6, 2);
    drawInitPosition(p, 6, 4);
    drawInitPosition(p, 6, 6);
    drawInitPosition(p, 6, 8);
    //上半部分炮的位置
    drawInitPosition(p, 2, 1);
    drawInitPosition(p, 2, 7);
    //下半部分炮的位置
    drawInitPosition(p, 7, 1);
    drawInitPosition(p, 7, 7);
}

//将棋盘的坐标转换成界面的坐标
QPoint Board :: center(int row, int col){
    QPoint ret;
    ret.rx() = (col+1)*_r*2;
    ret.ry() = (row+1)* _r*2;
    return ret;
}
//重载，如果传入的ID，也可以进行坐标的转换
QPoint Board :: center(int id){
    return center(_s[id]._row, _s[id]._col);
}

void Board::drawStone(QPainter& painter, int id){//画棋子的具体实现
    if(_s[id]._dead)
        return;
    QPoint c =center(id);
    QRect rect = QRect(c.x()-_r,c.y()-_r,_r*2,_r*2);

    if(id == _selectid)
        painter.setBrush(QBrush(Qt::gray));
    else
        painter.setBrush(QBrush(QColor(255,165,0)));

    if(_s[id]._red){
        painter.setPen(Qt::red);
        painter.drawEllipse(center(id), _r, _r);

        painter.drawEllipse(center(id), 16, 16);
    }else{
         painter.setPen(Qt::black);
         painter.drawEllipse(center(id), _r, _r);
         painter.drawEllipse(center(id), 16, 16);
    }
    painter.setFont(QFont("华文行楷", _r, 700));
    painter.drawText(rect, _s[id].getText(id), QTextOption(Qt::AlignCenter));
}
//判断棋子是否死亡
bool Board::isDead(int id){
    if(id == -1)return true;
    return _s[id]._dead;
}
//将鼠标所点击的像素坐标转换成棋盘的行列值，间接证明棋子是否存在-->存在效率问题
bool Board::isChecked(QPoint pt, int &row, int &col){
    for (row = 0; row <= 9; row++) {
        for (col = 0; col <= 8; col++) {
            QPoint c = center(row,col);
            int dx = c.x() - pt.x();
            int dy = c.y() - pt.y();
            int dist = dx*dx+dy*dy;
            if(dist < _r *_r)
                return true;
        }
    }
    return false;
}
//判断是不是在底部
bool Board::isBottomSide(int id){
    return _bSide == _s[id]._red;
}
//棋子的可落子位置限定
int Board::movableRange(int row1, int col1, int row, int col){
    return abs(row1-row)*10+abs(col1-col);
}
//判断某个坐标上有没有棋子，没有就会返回-1
int Board::getStoneId(int row, int col){
    for(int i=0; i<32; ++i){
        if(_s[i]._row == row && _s[i]._col == col && !isDead(i))
            return i;
    }
    return -1;
}
//得到两点之间有几个棋子
int Board::getStoneCountAtLine(int row1, int col1, int row2, int col2 ){
    int ret = 0;
    if(row1 != row2 && col1 != col2)
        return -1;
    if(row1 == row2 && col1 == col2)
        return -1;
    //计算同行两点之间有几个棋子
    if(row1 == row2){
       int min = col1 < col2 ? col1 : col2;
       int max = col1 < col2 ? col2 : col1;
       for(int col = min+1; col<max; ++col){
                if(getStoneId(row1, col) != -1)
                    ++ret;
             }
       //计算同列之间的两点之间有几个棋子
        }else{
            int min = row1 < row2 ? row1 : row2;
            int max = row1 < row2 ? row2 : row1;
            for(int row = min+1; row<max; ++row){
                if(getStoneId(row, col1) != -1)
                    ++ret;
            }
        }
        return ret;
}
//将军的走法
bool Board::canMoveJIANG(int moveid, int row, int col, int killid){
    /*
     * 1、存在一个飞将的功能在此之下没有步长限制，等同于车
     * 2、先拿到需要移动士的位置
     * 3、通过方法去计算移动位置
     * 4、确定移动范围
     * 5、确定步长
        */
    if(killid != -1 && _s[killid]._type == Stone::JIANG)
        return canMoveCHE(moveid, row, col, killid);

    GetRowCol(row1,col1,moveid);
    int mr = movableRange(row1, col1 ,row, col);
    if(mr == 1 || mr == 10){
        if((col >= 3 && col <= 5)){
            if(isBottomSide(moveid)){
                if(row >=7)
                    return true;
            }else{
                if(row <= 2)
                    return true;
            }
        }
    }
    return false;
}
//士的走法
bool Board::canMoveSHI(int moveid, int row, int col, int ){
    /*
     * 1、先拿到需要移动士的位置
     * 2、通过已实现的方法去计算移动位置
     * 3、制定棋子的一定范围
     * 4、在该范围内确定棋子的步长
     * 以下简便实现：
     */
    GetRowCol(row1,col1,moveid);
    int mr = movableRange(row1, col1 ,row, col);
    if(mr == 11){
        if((col >= 3 && col <= 5)){
            if(isBottomSide(moveid)){
                if(row >=7)
                    return true;
            }else{
                if(row <= 2)
                    return true;
            }
        }
    }
    return false;
}
//相的走法
bool Board::canMoveXIANG(int moveid, int row, int col, int){
    /*
     * 1、得到需要移动的相的位置
     * 2、通过方法确定可落位置
     * 3、判断在哪边
     * 4、在某一边的并且相不能过河的基础上判断存不存在相脚
     */
    GetRowCol(row1, col1, moveid);
    int mr = movableRange(row1, col1, row, col);
    if(mr == 22){
        if(isBottomSide(moveid)){
            if(row >= 4 ){
                int re = (row + row1) / 2;
                int ce = (col + col1) / 2;
                if(getStoneId(re,ce) == -1)
                    return true;
            }
        }else{
            if(row <= 5){
                int re = (row + row1) / 2;
                int ce = (col + col1) / 2;
                if(getStoneId(re,ce) == -1)
                    return true;
                }
            }
        }
    return false;
}
//马的走法
bool Board::canMoveMA(int moveid, int row, int col, int){
    /*
     * 1、拿到需要移动的馬的位置
     * 2、计算落子的位置
     * 3、在可落子的范围下找马脚
     * 4、需要注意的是mr=12与mr=21计算马脚的方式是不一样的不能混在一起
     */
    GetRowCol(row1, col1, moveid);
    int mr = movableRange(row1, col1, row, col);
    if(mr == 12 ){
        if(getStoneId(row1, (col+col1)/2) == -1)
            return true;
    }else if(mr == 21){
       if(getStoneId((row+row1)/2, col1) == -1)
            return true;
    }
    return false;
}
//车的走法
bool Board::canMoveCHE( int moveid, int row, int col, int ){
    /*
     * 1、先拿到需要操作的棋子当前位置
     * 2、通过已实现计数方法-->只有当两点之间不存在棋子才能走
     */
    GetRowCol(row1, col1, moveid);
    int ret = getStoneCountAtLine(row1, col1, row, col);
    if(ret == 0)
        return true;
    return false;
}

//炮的走法
bool Board::canMovePAO(int moveid, int row, int col, int killid){
    /*
     * 1、拿到要移动的炮的位置
     * 2、计算当前位置与目标位置有几个棋子
     * 3、当只有一个棋子并且隔着的棋子存在是可以动
     * 4、当中间没有棋子的时候可以动
     */
    GetRowCol(row1, col1, moveid);
    int count = getStoneCountAtLine(row, col, row1, col1);
    if(killid != -1){
        if(count == 1)
            return true;
    }else{
        if(count == 0)
            return true;
    }
    return false;
}
//兵的走法
bool Board::canMoveBING(int moveid, int row, int col, int ){
    /*
     * 1、得到要移动的兵的位置
     * 2、移动步长为1
     * 3、在满足步长为1且未过河的条件下只能往前走
     * 4、在满足步长为1且过河后可以往前左右走但不能往后走。
     */
    GetRowCol(row1,col1,moveid);
    int mr = movableRange(row1, col1, row, col);
    if(mr == 1 || mr == 10){
        if(isBottomSide(moveid)){
            if(row < 5 && row <= row1)
                return true;
            else if(row1 >= 5 && row < row1 )
                return true;
        }else{
            if(row > 4 && row >= row1)
                return true;
            if(row <= 4 && row > row1)
                return true;
        }
    }
    return false;
}

//棋子的走法
bool Board::canMove(int moveid, int row, int col, int killid){
    //不能杀自己的棋子
    if(isSameColor(moveid, killid)){
        _selectid = killid;
        update();
         return false;
    }

    if(!_isStart)return false;
    if(_isOverGame)return false;
    //通过选中的不同类棋子实施具体的走法
    switch (_s[moveid]._type) {
    case Stone::JIANG:
        return canMoveJIANG(moveid, row, col, killid);

    case Stone::SHI:
        return canMoveSHI(moveid, row, col, killid);

    case Stone::XIANG:
        return canMoveXIANG(moveid, row, col, killid);

    case Stone::MA:
        return canMoveMA(moveid, row, col, killid);

    case Stone::CHE:
        return canMoveCHE(moveid,  row, col, killid);

    case Stone::PAO:
        return canMovePAO(moveid, row, col, killid);

    case Stone::BING:
        return canMoveBING(moveid, row, col, killid);
    default: break;
    }
    return true;
}
//鼠标点击并离开事件
void Board :: mouseReleaseEvent(QMouseEvent *ev){
    click(ev->pos());
    if(_isOverGame){
        QMessageBox message(QMessageBox::Information, "提示", "本局已结束，请点击重新开始.");
        message.setFont(QFont("FangSong",16,QFont::Bold));
        message.exec();
    }
}
//悔棋
void Board::back(Step *step){
    /*
     * 1.复活死去的棋子
     * 2.棋子往回移动一步
     */
    reliveStone(step->_killid);
    moveStone(step->_moveid, step->_rowFrom, step->_colFrom);
}
void Board::backOne(){
    if(this->_steps.size() == 0) return;
    Step* step = this->_steps.last();
    _steps.removeLast();
    back(step);
    update();
    delete step;
}
void Board::back(){
    backOne();
}
//复活棋子
void Board::reliveStone(int id){
    if(id==-1) return;
    _s[id]._dead = false;
}
void Board::moveStone(int moveid, int row, int col){
    _s[moveid]._row = row;
    _s[moveid]._col = col;

    _bRedTurn = !_bRedTurn;
}
void Board::moveStone(int moveid, int killid, int row, int col){
    saveStep(moveid, killid, row, col, _steps);
    killStone(killid);
    moveStone(moveid, row, col);
    whoWin();
    if(killid== -1)
        _voice.voiceMove(); //移动音效
    else
        _voice.voiceEat(); //吃子音效
    if(isGeneral(_bRedTurn)){
        if(isHongMen() != 0){
            _voice.voiceGeneral();
            if(!isHaveAntherStep()){
                if(!_bRedTurn){
                    _timer->stop();
                    _isOverGame = true;
                    winMessageBox("提示", "绝杀！！！红方胜利");
                }else{
                    _timer->stop();
                    _isOverGame = true;
                    winMessageBox("提示", "绝杀！！！黑方胜利");
                }
            }
        }
        else
            backOne();
    }

    if(isGeneral()){
        backOne();
    }
}
//通过计算将帅之间是否有棋子间接判断有没有鸿门宴
int Board::isHongMen(){
    if(_s[4]._dead || _s[20]._dead)
        return 0;
    return getStoneCountAtLine(_s[4]._row, _s[4]._col, _s[20]._row, _s[20]._col);
}
//判断是否选中棋子
void Board::click(QPoint pt){
    int row, col;
    //将pt转成象棋的行列值
    //判断这个值上是否存在棋子
    bool bRet = isChecked(pt,row,col);
    if(!bRet)
        return;
    clickStone(getStoneId(row, col), row, col);
}

void Board::seleteStone(int id){
    if(id == -1 )return;
    if(!isBeSeleted(id))return;
    _selectid = id;
    update();
    _voice.voiceSelect();
}
bool Board::isBeSeleted(int id){
    return _bRedTurn == _s[id]._red;
}
void Board::testMoveStone(int killid, int row, int col){
    if(killid != -1 && isSameColor(_selectid, killid)){
        seleteStone(killid);
        return;
    }
    if(canMove(_selectid,  row, col, killid)){
        moveStone(_selectid, killid, row, col);

        _selectid = -1;
        update();
    }
}
bool Board::isSameColor(int moveid, int killid){
    if(killid == -1 || moveid == -1)return false;
    return isRed(moveid)== isRed(killid);
}
bool Board ::isRed(int id){
    return _s[id]._red;
}
void Board::clickStone(int clickid, int row, int col){
    if(_selectid == -1)
        seleteStone(clickid);
    else
        testMoveStone(clickid, row, col);
}
void Board::saveStep(int moveid, int killid, int row, int col, QVector<Step*>& steps){
    GetRowCol(row1, col1, moveid);
    Step* step = new Step;
    step->_colFrom = col1;
    step->_colTo = col;
    step->_rowFrom = row1;
    step->_rowTo = row;
    step->_moveid = moveid;
    step->_killid = killid;

    steps.append(step);
}
void Board::killStone(int id){
    if(id==-1) return;
    _s[id]._dead = true;
     _voice.voiceEat(); //吃子音效
}
//判断输赢
void Board::whoWin(){

    if(_s[4]._dead && !_s[20]._dead){
        _timer->stop();
        _isOverGame = true;
        winMessageBox("提示", "本局结束，黑方胜利！");
    }
    if(!_s[4]._dead && _s[20]._dead){
        _timer->stop();
        _isOverGame = true;
        winMessageBox("提示", "本局结束，红方胜利！");
    }
}

//轮到你走的时候是否会让自己的将军被杀
bool Board::isGeneral(){
    int generalId=20;
        if(!_bRedTurn)
            generalId=4;
        int row= _s[generalId]._row;
        int col= _s[generalId]._col;
        for(int i=0; i<32; ++i){
            if(canMove(i,row,col,generalId) && !_s[i]._dead){
                return true;
            }
        }
        return false;
}
//轮到你走的时候你可以不可以将对面
bool Board:: isGeneral(bool _bRedTurn){
    int generalId; 
    if(_bRedTurn){
         generalId=4;
         int row= _s[generalId]._row;
         int col= _s[generalId]._col;
         for(int i=0; i<32; ++i)
             if((canMove(i,row,col,generalId) && !_s[i]._dead)){
                 return true;
             }
    }else{
        generalId=20;
        int row= _s[generalId]._row;
        int col= _s[generalId]._col;
        for(int i=0; i<32; ++i)
            if((canMove(i,row,col,generalId) && !_s[i]._dead)){
                return true;
            }
    }
    return 0;
}
void Board::winMessageBox(QString title, QString msg){
    QMessageBox message(QMessageBox::Information, title, msg);
    message.setFont(QFont("FangSong", 16, QFont::Bold));
    message.exec();
}
//刷新时间
void Board::updateTime(){
    *_timeRecord = _timeRecord->addSecs(1);
    ui->lcdNumber->display(_timeRecord->toString("hh:mm:ss"));
    if(_isStart == false)
        ui->pushButton_start->setText("开始");
    else if(_isStart == true)
        ui->pushButton_start->setText("暂停");
}

void Board::on_pushButton_start_clicked(){
    //尚未开始-->开始计时
     _voice.voiceBackGroundMusicOn();
    if(!_isStart) {
            _timer->start(1000);
            ui->pushButton_start->setText("暂停");
        }else {//已经开始-->暂停
            _timer->stop();
            _voice.voiceBackGroundMusicOff();
            ui->pushButton_start->setText("继续");
        }
        _isStart = !_isStart;
}

void Board::on_pushButton_reset_clicked(){
    _timer->stop();    //计时器停止
    _timeRecord->setHMS(0,0,0); //时间设为0
    ui->lcdNumber->display(_timeRecord->toString("hh:mm:ss")); //显示00:00:00
    _isStart = false;
    ui->pushButton_start->setText("开始");
    ui->pushButton_start->setEnabled(true);
}
void Board :: on_pushButton_restart_clicked(){
    if(_isStart){
     on_pushButton_reset_clicked();
     _isStart = false;
    }
    init(true);
}
void Board::on_pushButton_back_clicked(){
    backOne();
    update();
}


void Board::on_pushButton_toMenu_clicked(){
    emit this->toMenu();
}
/**
 * @brief Board::getAllPossibleMove-->拿到所有棋子可能走的地方
 * @param steps1
 */
void Board::getAllPossibleMove(QVector<Step *> &steps1){
    int min, max;
    if(this->_bRedTurn){
        min = 0, max = 16;
    }else{
        min = 16, max = 32;
    }
    for(int i=min;i<max; i++){
        if(this->_s[i]._dead) continue;
        for(int row = 0; row<=9; ++row){
            for(int col=0; col<=8; ++col){
                int killid = this->getStoneId(row, col);
                if(isSameColor(i, killid)) continue;
                if(canMove(i, row, col, killid)){
                    Board::saveStep(i, killid, row, col, steps1);
                }
            }
        }
    }
}

/**
 * @brief Board::isHaveAntherStep-->判断移动完棋子是否处于被将军的状态
 * @return
 */
bool Board :: isHaveAntherStep(){
    QVector<Step *> steps1;
    bool isGen = false;
    if(_bRedTurn){
        getAllPossibleMove(steps1);
        for(QVector<Step*> :: iterator it = steps1.begin();it != steps1.end();it++){
            Step* step = *it;
            fakeMove(step);
            if(isGeneral()){
                unFakeMove(step);
                continue;
            }
            else{
                isGen = true;
                unFakeMove(step);
            }
        }
    }else {
        getAllPossibleMove(steps1);
        for(QVector<Step*> :: iterator it = steps1.begin();it != steps1.end();it++){
            Step* step = *it;
            fakeMove(step);
            if(isGeneral()){
                unFakeMove(step);
                continue;
            }
            else{
                isGen = true;
                unFakeMove(step);
            }
        }
    }
    return isGen;
}

void Board::fakeMove(Step *step){
    killStone(step->_killid);
    moveStone(step->_moveid, step->_rowTo, step->_colTo);
}

void Board::unFakeMove(Step *step){
    reliveStone(step->_killid);
    moveStone(step->_moveid, step->_rowFrom, step->_colFrom);
}
